/**
 * @properties={typeid:35,uuid:"e16e9730-1a67-4617-8968-ebbec2a0725b"}
 */
var mod_js_core_testLog = '';

/**
 * @properties={typeid:35,uuid:"15ca10c0-8da9-4685-a92e-4ae64c295b7b"}
 */
var mod_js_core_testLog_html = '';

/**
 * @properties={typeid:35,uuid:"59108fc6-3718-4e55-974d-9f0aa83dc290"}
 */
var mod_js_core_testMessage = '';

/**
 * @properties={typeid:35,uuid:"96356049-7dad-4656-976f-794604a6151b"}
 */
var mod_js_core_testSummary = '';

/**
 * @properties={typeid:24,uuid:"57d6dd4b-3e5e-42d7-b97b-e6158df69917"}
 */
function mod_js_core_extend_Array()
{
	Array.from = $A;
	Object.extend(Array.prototype, Enumerable);
	if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;

	Object.extend(Array.prototype, {
	  _each: function(iterator) {
	    for (var i = 0, length = this.length; i < length; i++)
	      iterator(this[i]);
	  },
	
	  clear: function() {
	    this.length = 0;
	    return this;
	  },
	
	  first: function() {
	    return this[0];
	  },
	
	  last: function() {
	    return this[this.length - 1];
	  },
	
	  compact: function() {
	    return this.select(function(value) {
	      return value != null;
	    });
	  },
	
	  flatten: function() {
	    return this.inject([], function(array, value) {
	      return array.concat(Object.isArray(value) ?
	        value.flatten() : [value]);
	    });
	  },
	
	  without: function() {
	    var values = $A(arguments);
	    return this.select(function(value) {
	      return !values.include(value);
	    });
	  },
	
	  reverse: function(inline) {
	    return (inline !== false ? this : this.toArray())._reverse();
	  },
	
	  reduce: function() {
	    return this.length > 1 ? this : this[0];
	  },
	
	  uniq: function(sorted) {
	    return this.inject([], function(array, value, index) {
	      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
	        array.push(value);
	      return array;
	    });
	  },
	
	  intersect: function(array) {
	    return this.uniq().findAll(function(item) {
	      return array.detect(function(value) { return item === value });
	    });
	  },
	
	  clone: function() {
	    return [].concat(this);
	  },
	
	  size: function() {
	    return this.length;
	  },
	
	  inspect: function() {
	    return '[' + this.map(Object.inspect).join(', ') + ']';
	  },
	
	  toJSON: function() {
	    var results = [];
	    this.each(function(object) {
	      var value = Object.toJSON(object);
	      if (!Object.isUndefined(value)) results.push(value);
	    });
	    return '[' + results.join(', ') + ']';
	  }
	});
}

/**
 * @properties={typeid:24,uuid:"8480a659-c6ed-4f6d-bbd4-31f8fb6a9c4a"}
 */
function mod_js_core_extend_Class()
{
	Class = {
	  create: function() {
	    var parent = null, properties = $A(arguments);
	    if (Object.isFunction(properties[0]))
	      parent = properties.shift();
	
	    function klass() {
	      this.initialize.apply(this, arguments);
	    }
	
	    Object.extend(klass, Class.Methods);
	    klass.superclass = parent;
	    klass.subclasses = [];
	
	    if (parent) {
	      var subclass = function() { };
	      subclass.prototype = parent.prototype;
	      klass.prototype = new subclass;
	      parent.subclasses.push(klass);
	    }
	
	    for (var i = 0; i < properties.length; i++)
	      klass.addMethods(properties[i]);
	
	    if (!klass.prototype.initialize)
	      klass.prototype.initialize = Prototype.emptyFunction;
	
	    klass.prototype.constructor = klass;
	
	    return klass;
	  }
	};
	
	Class.Methods = {
	  addMethods: function(source) {
	    var ancestor   = this.superclass && this.superclass.prototype;
	    var properties = Object.keys(source);
	
	    if (!Object.keys({ toString: true }).length)
	      properties.push("toString", "valueOf");
	
	    for (var i = 0, length = properties.length; i < length; i++) {
	      var property = properties[i], value = source[property];
	      if (ancestor && Object.isFunction(value) &&
	          value.argumentNames().first() == "$super") {
	        var method = value, value = Object.extend((function(m) {
	          return function() { return ancestor[m].apply(this, arguments) };
	        })(property).wrap(method), {
	          valueOf:  function() { return method },
	          toString: function() { return method.toString() }
	        });
	      }
	      this.prototype[property] = value;
	    }
	
	    return this;
	  }
	};

	var Abstract = { };
}

/**
 * @properties={typeid:24,uuid:"0a7d9031-37ee-4f30-98fe-e523fd954118"}
 */
function mod_js_core_extend_Date()
{
	Date.prototype.toJSON = function() {
	  return '"' + this.getUTCFullYear() + '-' +
	    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
	    this.getUTCDate().toPaddedString(2) + 'T' +
	    this.getUTCHours().toPaddedString(2) + ':' +
	    this.getUTCMinutes().toPaddedString(2) + ':' +
	    this.getUTCSeconds().toPaddedString(2) + 'Z"';
	};
}

/**
 * @properties={typeid:24,uuid:"89d6c7c9-5310-4d0f-adb6-3a05bc8c5847"}
 */
function mod_js_core_extend_Enumerable()
{
	$break = { };
		
	Enumerable = {
	  each: function(iterator, context) {
	    var index = 0;
	    iterator = iterator.bind(context);
	    try {
	      this._each(function(value) {
	        iterator(value, index++);
	      });
	    } catch (e) {
	      if (e != $break) throw e;
	    }
	    return this;
	  },
	
	  eachSlice: function(number, iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var index = -number, slices = [], array = this.toArray();
	    while ((index += number) < array.length)
	      slices.push(array.slice(index, index+number));
	    return slices.collect(iterator, context);
	  },
	
	  all: function(iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var result = true;
	    this.each(function(value, index) {
	      result = result && !!iterator(value, index);
	      if (!result) throw $break;
	    });
	    return result;
	  },
	
	  any: function(iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var result = false;
	    this.each(function(value, index) {
	      if (result = !!iterator(value, index))
	        throw $break;
	    });
	    return result;
	  },
	
	  collect: function(iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var results = [];
	    this.each(function(value, index) {
	      results.push(iterator(value, index));
	    });
	    return results;
	  },
	
	  detect: function(iterator, context) {
	    iterator = iterator.bind(context);
	    var result;
	    this.each(function(value, index) {
	      if (iterator(value, index)) {
	        result = value;
	        throw $break;
	      }
	    });
	    return result;
	  },
	
	  findAll: function(iterator, context) {
	    iterator = iterator.bind(context);
	    var results = [];
	    this.each(function(value, index) {
	      if (iterator(value, index))
	        results.push(value);
	    });
	    return results;
	  },
	
	  grep: function(filter, iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var results = [];
	
	    if (Object.isString(filter))
	      filter = new RegExp(filter);
	
	    this.each(function(value, index) {
	      if (filter.match(value))
	        results.push(iterator(value, index));
	    });
	    return results;
	  },
	
	  include: function(object) {
	    if (Object.isFunction(this.indexOf))
	      if (this.indexOf(object) != -1) return true;
	
	    var found = false;
	    this.each(function(value) {
	      if (value == object) {
	        found = true;
	        throw $break;
	      }
	    });
	    return found;
	  },
	
	  inGroupsOf: function(number, fillWith) {
	    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
	    return this.eachSlice(number, function(slice) {
	      while(slice.length < number) slice.push(fillWith);
	      return slice;
	    });
	  },
	
	  inject: function(memo, iterator, context) {
	    iterator = iterator.bind(context);
	    this.each(function(value, index) {
	      memo = iterator(memo, value, index);
	    });
	    return memo;
	  },
	
	  invoke: function(method) {
	    var args = $A(arguments).slice(1);
	    return this.map(function(value) {
	      return value[method].apply(value, args);
	    });
	  },
	
	  max: function(iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var result;
	    this.each(function(value, index) {
	      value = iterator(value, index);
	      if (result == null || value >= result)
	        result = value;
	    });
	    return result;
	  },
	
	  min: function(iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var result;
	    this.each(function(value, index) {
	      value = iterator(value, index);
	      if (result == null || value < result)
	        result = value;
	    });
	    return result;
	  },
	
	  partition: function(iterator, context) {
	    iterator = iterator ? iterator.bind(context) : Prototype.K;
	    var trues = [], falses = [];
	    this.each(function(value, index) {
	      (iterator(value, index) ?
	        trues : falses).push(value);
	    });
	    return [trues, falses];
	  },
	
	  pluck: function(property) {
	    var results = [];
	    this.each(function(value) {
	      results.push(value[property]);
	    });
	    return results;
	  },
	
	  reject: function(iterator, context) {
	    iterator = iterator.bind(context);
	    var results = [];
	    this.each(function(value, index) {
	      if (!iterator(value, index))
	        results.push(value);
	    });
	    return results;
	  },
	
	  sortBy: function(iterator, context) {
	    iterator = iterator.bind(context);
	    return this.map(function(value, index) {
	      return {value: value, criteria: iterator(value, index)};
	    }).sort(function(left, right) {
	      var a = left.criteria, b = right.criteria;
	      return a < b ? -1 : a > b ? 1 : 0;
	    }).pluck('value');
	  },
	
	  toArray: function() {
	    return this.map();
	  },
	
	  zip: function() {
	    var iterator = Prototype.K, args = $A(arguments);
	    if (Object.isFunction(args.last()))
	      iterator = args.pop();
	
	    var collections = [this].concat(args).map($A);
	    return this.map(function(value, index) {
	      return iterator(collections.pluck(index));
	    });
	  },
	
	  size: function() {
	    return this.toArray().length;
	  },
	
	  inspect: function() {
	    return '#<Enumerable:' + this.toArray().inspect() + '>';
	  }
	};
	
	Object.extend(Enumerable, {
	  map:     Enumerable.collect,
	  find:    Enumerable.detect,
	  select:  Enumerable.findAll,
	  filter:  Enumerable.findAll,
	  member:  Enumerable.include,
	  entries: Enumerable.toArray,
	  every:   Enumerable.all,
	  some:    Enumerable.any
	});
}

/**
 * @properties={typeid:24,uuid:"3ec1bc90-bc15-4dd3-b96b-0f4d7385e16c"}
 */
function mod_js_core_extend_Function()
{
	Object.extend(Function.prototype, {
	  argumentNames: function() {
	    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
	    return names.length == 1 && !names[0] ? [] : names;
	  },
	
	  bind: function() {
	    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
	    var __method = this, args = $A(arguments), object = args.shift();
	    return function() {
	      return __method.apply(object, args.concat($A(arguments)));
	    }
	  },
	
	  bindAsEventListener: function() {
	    var __method = this, args = $A(arguments), object = args.shift();
	    return function(event) {
	      return __method.apply(object, [event || window.event].concat(args));
	    }
	  },
	
	  curry: function() {
	    if (!arguments.length) return this;
	    var __method = this, args = $A(arguments);
	    return function() {
	      return __method.apply(this, args.concat($A(arguments)));
	    }
	  },
	
	  wrap: function(wrapper) {
	    var __method = this;
	    return function() {
	      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
	    }
	  },
	
	  methodize: function() {
	    if (this._methodized) return this._methodized;
	    var __method = this;
	    return this._methodized = function() {
	      return __method.apply(null, [this].concat($A(arguments)));
	    };
	  }
	});

}

/**
 * @properties={typeid:24,uuid:"43666951-1bfb-459b-8181-10a540f352b3"}
 */
function mod_js_core_extend_Globals()
{
	Prototype = {	
	  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
	
	  emptyFunction: function() { },
	  K: function(x) { return x }
	};

	// toArray shortcut
	$A = function $A(iterable) {
	  if (!iterable) return [];
	  if (iterable.toArray) return iterable.toArray();
	  var length = iterable.length || 0, results = new Array(length);
	  while (length--) results[length] = iterable[length];
	  return results;
	}
	
	// String to array parser
	$w = function $w(string) {
	  if (!Object.isString(string)) return [];
	  string = string.strip();
	  return string ? string.split(/\s+/) : [];
	}
	
	$H = function $H(object) {
  	return new Hash(object);
	};
}

/**
 * @properties={typeid:24,uuid:"1c558e3d-4726-4925-b0d2-5292633333fa"}
 */
function mod_js_core_extend_Hash()
{
	Hash = Class.create(Enumerable, (function() {
	  function toQueryPair(key, value) {
	    if (Object.isUndefined(value)) return key;
	    return key + '=' + encodeURIComponent(String.interpret(value));
	  }
	
	  return {
	    initialize: function(object) {
	  		var obj = object || { };
	      this._object = Object.isHash(obj) ? object.toObject() : Object.clone(obj);
	  	},
	
	   _each: function(iterator) {
	      for (var key in this._object) {
	        var value = this._object[key], pair = [key, value];
	        pair.key = key;
	        pair.value = value;
	        iterator(pair);
	      }
	    },
	
	    set: function(key, value) {
	      return this._object[key] = value;
	    },
	
	    get: function(key) {
	      return this._object[key];
	    },
	
	    unset: function(key) {
	      var value = this._object[key];
	      delete this._object[key];
	      return value;
	    },
	
	    toObject: function() {
	      return Object.clone(this._object);
	    },
	
	    keys: function() {
	      return this.pluck('key');
	    },
	
	    values: function() {
	      return this.pluck('value');
	    },
	
	    index: function(value) {
	      var match = this.detect(function(pair) {
	        return pair.value === value;
	      });
	      return match && match.key;
	    },
	
	    merge: function(object) {
	      return this.clone().update(object);
	    },
	
	    update: function(object) {
	      return new Hash(object).inject(this, function(result, pair) {
	        result.set(pair.key, pair.value);
	        return result;
	      });
	    },
	
	    toQueryString: function() {
	      return this.map(function(pair) {
	        var key = encodeURIComponent(pair.key), values = pair.value;
	
	        if (values && typeof values == 'object') {
	          if (Object.isArray(values))
	            return values.map(toQueryPair.curry(key)).join('&');
	        }
	        return toQueryPair(key, values);
	      }).join('&');
	    },
	
	    inspect: function() {
	      return '#<Hash:{' + this.map(function(pair) {
	        return pair.map(Object.inspect).join(': ');
	      }).join(', ') + '}>';
	    },
	
	    toJSON: function() {
	      return Object.toJSON(this.toObject());
	    },
	
	    clone: function() {
	      return new Hash(this);
	    }
	  }
	})());
	
	Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
	Hash.from = $H;
}

/**
 * @properties={typeid:24,uuid:"f7a480e4-431b-46a9-86ec-b72c51228345"}
 */
function mod_js_core_extend_Object()
{
	// Take a destination and source object, and copies the properties of
	// source object to the destination object
	Object.extend = function(destination, source) {
  	for (var property in source)
    	destination[property] = source[property];
  	return destination;
	};
	
	Object.extend(Object, {
		  inspect: function(object) {
		    try {
		      if (Object.isUndefined(object)) return 'undefined';
		      if (object === null) return 'null';
		      return object.inspect ? object.inspect() : String(object);
		    } catch (e) {
		      if (e instanceof RangeError) return '...';
		      throw e;
		    }
		  },

		  toJSON: function(object) {
	  	var type = typeof object;
	  	switch (type) {
	  		case 'undefined':
				case 'function':
				case 'unknown': return;
				case 'boolean': return object.toString();
			}
	
			if (object === null) return 'null';
			if (object.toJSON) return object.toJSON();
	
			var results = [];
			for (var property in object) {
	      var value = Object.toJSON(object[property]);
	      if (!Object.isUndefined(value))
	        results.push(property.toJSON() + ': ' + value);
	    }
	    return '{' + results.join(', ') + '}';
		},
	 
		keys: function(object) {
			var keys = [];
    	for (var property in object)
    	  keys.push(property);
    	return keys;
  	},

		values: function(object) {
    	var values = [];
    	for (var property in object)
    	  values.push(object[property]);
    	return values;
  	},
  	
  	clone: function(object) {
    	return Object.extend({ }, object);
  	},
	
	  isArray: function(object) {
	    return object != null && typeof object == "object" &&
	      'splice' in object && 'join' in object;
	  },
	
	  isHash: function(object) {
	  	if(!object) return false;
	    return object instanceof Hash;
	  },
	
	  isFunction: function(object) {
	  	var fl = (typeof object == "function");
	  	// 2008.09.02 by Greg Pierce - work around Rhino bug (typeof /foo/ == 'function')
	  	if(fl && object['exec'] && object['test']) return false;
	  	return fl;
	  },
	
	  isString: function(object) {
	    return typeof object == "string";
	  },
	
	  isNumber: function(object) {
	    return typeof object == "number";
	  },
	
	  isUndefined: function(object) {
	    return typeof object == "undefined";
	  }
	});
}

/**
 * @properties={typeid:24,uuid:"07d73248-dcac-4af5-8215-5cf08a21a91c"}
 */
function mod_js_core_extend_String()
{
	Object.extend(String, {
	  interpret: function(value) {
	    return value == null ? '' : String(value);
	  },
	  specialChar: {
	    '\b': '\\b',
	    '\t': '\\t',
	    '\n': '\\n',
	    '\f': '\\f',
	    '\r': '\\r',
	    '\\': '\\\\'
	  }
	});
		
	Object.extend(String.prototype, {
	  gsub: function(pattern, replacement) {
	    var result = '', source = this, match;
	    replacement = arguments.callee.prepareReplacement(replacement);
	
	    while (source.length > 0) {
	      if (match = source.match(pattern)) {
	        result += source.slice(0, match.index);
	        result += String.interpret(replacement(match));
	        source  = source.slice(match.index + match[0].length);
	      } else {
	        result += source, source = '';
	      }
	    }
	    return result;
	  },
	
	  sub: function(pattern, replacement, count) {
	    replacement = this.gsub.prepareReplacement(replacement);
	    count = Object.isUndefined(count) ? 1 : count;
	
	    return this.gsub(pattern, function(match) {
	      if (--count < 0) return match[0];
	      return replacement(match);
	    });
	  },
	
	  scan: function(pattern, iterator) {
	    this.gsub(pattern, iterator);
	    return String(this);
	  },
	
	  truncate: function(length, truncation) {
	    length = length || 30;
	    truncation = Object.isUndefined(truncation) ? '...' : truncation;
	    return this.length > length ?
	      this.slice(0, length - truncation.length) + truncation : String(this);
	  },
	
	  strip: function() {
	    return this.replace(/^\s+/, '').replace(/\s+$/, '');
	  },
	
	  stripTags: function() {
	    return this.replace(/<\/?[^>]+>/gi, '');
	  },
	
	  toArray: function() {
	    return this.split('');
	  },
	
	  succ: function() {
	    return this.slice(0, this.length - 1) +
	      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
	  },
	
	  times: function(count) {
	    return count < 1 ? '' : new Array(count + 1).join(this);
	  },
	
	  camelize: function() {
	    var parts = this.split('-'), len = parts.length;
	    if (len == 1) return parts[0];
	
	    var camelized = this.charAt(0) == '-'
	      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
	      : parts[0];
	
	    for (var i = 1; i < len; i++)
	      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
	
	    return camelized;
	  },
	
	  capitalize: function() {
	    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
	  },
	
	  underscore: function() {
	    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
	  },
	
	  dasherize: function() {
	    return this.gsub(/_/,'-');
	  },
	
	  inspect: function(useDoubleQuotes) {
	    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
	      var character = String.specialChar[match[0]];
	      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
	    });
	    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
	    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
	  },
	
	  toJSON: function() {
	    return this.inspect(true);
	  },
	
	  unfilterJSON: function(filter) {
	    return this.sub(filter || Prototype.JSONFilter, '#{1}');
	  },
	
	  //isJSON: function() {
	  //  var str = this;
	  //  if (str.blank()) return false;
	  //  str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
	  //  return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
	  //},
	
	  evalJSON: function(sanitize) {
	    var json = this.unfilterJSON();
	    try {
	      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
	    } catch (e) { }
	    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
	  },
	
	  include: function(pattern) {
	    return this.indexOf(pattern) > -1;
	  },
	
	  startsWith: function(pattern) {
	    return this.indexOf(pattern) === 0;
	  },
	
	  endsWith: function(pattern) {
	    var d = this.length - pattern.length;
	    return d >= 0 && this.lastIndexOf(pattern) === d;
	  },
	
	  empty: function() {
	    return this == '';
	  },
	
	  blank: function() {
	    return /^\s*$/.test(this);
	  },
	
	  interpolate: function(object, pattern) {
	    return new Template(this, pattern).evaluate(object);
	  }
	});
	    
	String.prototype.gsub.prepareReplacement = function(replacement) {
	  if (Object.isFunction(replacement)) return replacement;
	  var template = new Template(replacement);
	  return function(match) { return template.evaluate(match) };
	};    

	Template = Class.create({
	  initialize: function(template, pattern) {
	    this.template = template.toString();
	    this.pattern = pattern || Template.Pattern;
	  },
	
	  evaluate: function(object) {
	    if (Object.isFunction(object.toTemplateReplacements))
	      object = object.toTemplateReplacements();
	
	    return this.template.gsub(this.pattern, function(match) {
	      if (object == null) return '';
	
	      var before = match[1] || '';
	      if (before == '\\') return match[2];
	
	      var ctx = object, expr = match[3];
	      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
	      match = pattern.exec(expr);
	      if (match == null) return before;
	
	      while (match != null) {
	        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
	        ctx = ctx[comp];
	        if (null == ctx || '' == match[3]) break;
	        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
	        match = pattern.exec(expr);
	      }
	
	      return before + String.interpret(ctx);
	    });
	  }
	});
	Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
}

/**
 * @properties={typeid:24,uuid:"c197a105-3587-4c85-8330-412a4d75b483"}
 */
function mod_js_core_extend_TestBDD()
{
	Test.setupBDDExtensionMethods = function(){
	  var METHODMAP = {
	    shouldEqual:     'assertEqual',
	    shouldNotEqual:  'assertNotEqual',
	    shouldEqualEnum: 'assertEnumEqual',
	    shouldBeA:       'assertType',
	    shouldNotBeA:    'assertNotOfType',
	    shouldBeAn:      'assertType',
	    shouldNotBeAn:   'assertNotOfType',
	    shouldBeNull:    'assertNull',
	    shouldNotBeNull: 'assertNotNull',
	    
	    shouldBe:        'assertReturnsTrue',
	    shouldNotBe:     'assertReturnsFalse',
	    shouldRespondTo: 'assertRespondsTo'
	  };
	  var makeAssertion = function(assertion, args, object) { 
	   	this[assertion].apply(this,(args || []).concat([object]));
	  }
	  
	  Test.BDDMethods = {};   
	  $H(METHODMAP).each(function(pair) { 
	    Test.BDDMethods[pair.key] = function() { 
	       var args = $A(arguments); 
	       var scope = args.shift(); 
	       makeAssertion.apply(scope, [pair.value, args, this]); }; 
	  });
	  
	  [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
	    function(p){ Object.extend(p, Test.BDDMethods) }
	  );
	}
	
	Test.context = function(name, spec, log){
	  Test.setupBDDExtensionMethods();
	  
	  var compiledSpec = {};
	  var titles = {};
	  for(specName in spec) {
	    switch(specName){
	      case "setup":
	      case "teardown":
	        compiledSpec[specName] = spec[specName];
	        break;
	      default:
	        var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
	        var body = spec[specName].toString().split('\n').slice(1);
	        if(/^\{/.test(body[0])) body = body.slice(1);
	        body.pop();
	        body = body.map(function(statement){ 
	          return statement.strip()
	        });
	        compiledSpec[testName] = body.join('\n');
	        titles[testName] = specName;
	    }
	  }
	  new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
	};		
}

/**
 * @properties={typeid:24,uuid:"d9a8698b-5842-4a6f-9538-ca3ae8e6fa3c"}
 */
function mod_js_core_extend_TestUnit()
{
	Test = {};
	Test.Unit = {};
	Test.Unit.inspect = Object.inspect;
	
	// Assertions
	Test.Unit.Assertions = Class.create();
	Test.Unit.Assertions.prototype = {
	  initialize: function() {
	    this.assertions = 0;
	    this.failures   = 0;
	    this.errors     = 0;
	    this.messages   = [];
	  },
	  summary: function() {
	    return (
	      this.assertions + " assertions, " + 
	      this.failures   + " failures, " +
	      this.errors     + " errors" + "\n" +
	      this.messages.join("\n"));
	  },
	  pass: function() {
	    this.assertions++;
	  },
	  fail: function(message) {
	    this.failures++;
	    this.messages.push("Failure: " + message);
	  },
	  info: function(message) {
	    this.messages.push("Info: " + message);
	  },
	  error: function(error) {
	    this.errors++;
	    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
	  },
	  status: function() {
	    if (this.failures > 0) return 'failed';
	    if (this.errors > 0) return 'error';
	    return 'passed';
	  },
	  assert: function(expression) {
	    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
	    try { expression ? this.pass() : 
	      this.fail(message); }
	    catch(e) { this.error(e); }
	  },
	  assertEqual: function(expected, actual) {
	    var message = arguments[2] || "assertEqual";
	    try { (expected == actual) ? this.pass() :
	      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
	        '", actual "' + Test.Unit.inspect(actual) + '"'); }
	    catch(e) { this.error(e); }
	  },
	  assertInspect: function(expected, actual) {
	    var message = arguments[2] || "assertInspect";
	    try { (expected == actual.inspect()) ? this.pass() :
	      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
	        '", actual "' + Test.Unit.inspect(actual) + '"'); }
	    catch(e) { this.error(e); }
	  },
	  assertEnumEqual: function(expected, actual) {
	    var message = arguments[2] || "assertEnumEqual";
	    try { $A(expected).length == $A(actual).length && 
	      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
	        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
	          ', actual ' + Test.Unit.inspect(actual)); }
	    catch(e) { this.error(e); }
	  },
	  assertNotEqual: function(expected, actual) {
	    var message = arguments[2] || "assertNotEqual";
	    try { (expected != actual) ? this.pass() : 
	      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
	    catch(e) { this.error(e); }
	  },
	  assertIdentical: function(expected, actual) { 
	    var message = arguments[2] || "assertIdentical"; 
	    try { (expected === actual) ? this.pass() : 
	      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
	        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
	    catch(e) { this.error(e); } 
	  },
	  assertNotIdentical: function(expected, actual) { 
	    var message = arguments[2] || "assertNotIdentical"; 
	    try { !(expected === actual) ? this.pass() : 
	      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
	        '", actual "' + Test.Unit.inspect(actual) + '"'); } 
	    catch(e) { this.error(e); } 
	  },
	  assertNull: function(obj) {
	    var message = arguments[1] || 'assertNull'
	    try { (obj==null) ? this.pass() : 
	      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
	    catch(e) { this.error(e); }
	  },
	  assertMatch: function(expected, actual) {
	    var message = arguments[2] || 'assertMatch';
	    var regex = new RegExp(expected);
	    try { (regex.exec(actual)) ? this.pass() :
	      this.fail(message + ' : regex: "' +  Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
	    catch(e) { this.error(e); }
	  },
	  assertHidden: function(element) {
	    var message = arguments[1] || 'assertHidden';
	    this.assertEqual("none", element.style.display, message);
	  },
	  assertNotNull: function(object) {
	    var message = arguments[1] || 'assertNotNull';
	    this.assert(object != null, message);
	  },
	  assertType: function(expected, actual) {
	    var message = arguments[2] || 'assertType';
	    try { 
	      (actual.constructor == expected) ? this.pass() : 
	      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
	        '", actual "' + (actual.constructor) + '"'); }
	    catch(e) { this.error(e); }
	  },
	  assertNotOfType: function(expected, actual) {
	    var message = arguments[2] || 'assertNotOfType';
	    try { 
	      (actual.constructor != expected) ? this.pass() : 
	      this.fail(message + ': expected "' + Test.Unit.inspect(expected) +  
	        '", actual "' + (actual.constructor) + '"'); }
	    catch(e) { this.error(e); }
	  },
	  assertInstanceOf: function(expected, actual) {
	    var message = arguments[2] || 'assertInstanceOf';
	    try { 
	      (actual instanceof expected) ? this.pass() : 
	      this.fail(message + ": object was not an instance of the expected type"); }
	    catch(e) { this.error(e); } 
	  },
	  assertNotInstanceOf: function(expected, actual) {
	    var message = arguments[2] || 'assertNotInstanceOf';
	    try { 
	      !(actual instanceof expected) ? this.pass() : 
	      this.fail(message + ": object was an instance of the not expected type"); }
	    catch(e) { this.error(e); } 
	  },
	  assertRespondsTo: function(method, obj) {
	    var message = arguments[2] || 'assertRespondsTo';
	    try {
	      (obj[method] && typeof obj[method] == 'function') ? this.pass() : 
	      this.fail(message + ": object doesn't respond to [" + method + "]"); }
	    catch(e) { this.error(e); }
	  },
	  assertReturnsTrue: function(method, obj) {
	    var message = arguments[2] || 'assertReturnsTrue';
	    try {
	      var m = obj[method];
	      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
	      m() ? this.pass() : 
	      this.fail(message + ": method returned false"); }
	    catch(e) { this.error(e); }
	  },
	  assertReturnsFalse: function(method, obj) {
	    var message = arguments[2] || 'assertReturnsFalse';
	    try {
	      var m = obj[method];
	      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
	      !m() ? this.pass() : 
	      this.fail(message + ": method returned true"); }
	    catch(e) { this.error(e); }
	  },
	  assertRaise: function(exceptionName, method) {
	    var message = arguments[2] || 'assertRaise';
	    try { 
	      method();
	      this.fail(message + ": exception expected but none was raised"); }
	    catch(e) {
	      ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e); 
	    }
	  },
	  assertNothingRaised: function(method) {
	  	var message = arguments[2] || 'assertNothingRaised';
	  	try {
	  		method();
	  		this.pass(); }
	  	catch(e) {
	  		this.fail(message + ": exception was thrown when none was expected.")
	  	}
	  },
	  assertUndefined: function(obj, message) {
	    var message = arguments[2] || 'assertUndefined';
	    try { typeof obj == "undefined" ? this.pass() :
	    	this.fail(message + ": expected 'undefined', but was type '" + typeof obj + "'");
	    }
	    catch(e) { this.error(e) }
	  },	  
	  assertNotUndefined: function(obj) {
	  	var message = arguments[2] || 'assertNotUndefined';
	    try { typeof obj != "undefined" ? this.pass() :
	    	this.fail(message + ": expected not undefined, but was undefined");
	    }
	    catch(e) { this.error(e) }
	  },  
	  benchmark: function(operation, iterations) {
	    var startAt = new Date();
	    (iterations || 1).times(operation);
	    var timeTaken = ((new Date())-startAt);
	    this.info((arguments[2] || 'Operation') + ' finished ' + 
	       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
	    return timeTaken;
	  }
	}
	
	// TestCase
	Test.Unit.Testcase = Class.create();
	Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
	  initialize: function(name, test, setup, teardown) {
	    Test.Unit.Assertions.prototype.initialize.bind(this)();
	    this.name           = name;
	    
	    if(typeof test == 'string') {
	      test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
	      test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
	      this.test = function() {
	        eval('with(this){'+test+'}');
	      }
	    } else {
	      this.test = test || function() {};
	    }
	    
	    this.setup          = setup || function() {};
	    this.teardown       = teardown || function() {};
	    this.isWaiting      = false;
	    this.timeToWait     = 1000;
	  },
	  wait: function(time, nextPart) {
	    this.isWaiting = true;
	    this.test = nextPart;
	    this.timeToWait = time;
	  },
	  run: function() {
	    try {
	      try {
	        if (!this.isWaiting) this.setup.bind(this)();
	        this.isWaiting = false;
	        this.test.bind(this)();
	      } finally {
	        if(!this.isWaiting) {
	          this.teardown.bind(this)();
	        }
	      }
	    }
	    catch(e) { this.error(e); }
	  }
	});
}

/**
 * @properties={typeid:24,uuid:"c0c56373-b195-43ee-8dd5-45a7fef65eb2"}
 */
function mod_js_core_extend_TestUnitLogger()
{
	Test.Unit.Logger = Class.create();
	Test.Unit.Logger.prototype = {
	  initialize: function(log) { 
	  	globals.mod_js_core_testLog = '';
	  	globals.mod_js_core_testLog_html = '<html><head>\n';
	  	globals.mod_js_core_testLog_html += '<style>body{font:12pt Tahoma;}\np{margin:0 4pt 0 10pt;}\n.failed{color:red;font-weight:bold;}\n.passed{color:green;font-weight:bold;}</style>';
	  	globals.mod_js_core_testLog_html += '</head><body>\n#{SUMMARY}\n';
	  },
	  start: function(testName) {
	    this.testName = testName;
	    if(!this.testName) this.testName = 'Test';
			globals.mod_js_core_testLog += this.testName;
			globals.mod_js_core_testLog_html += '<h4>' + this.testName + '</h4>\n';
	  },
	  finish: function(status, summary) {
			globals.mod_js_core_testLog += ", " + status + ", " + summary + '\n';
			globals.mod_js_core_testLog_html += '<p><span class="' + status + '">' + status + ':</span>\n<i>' + summary.split('\n').join('<br>\n') + '</i></p>';
	  },
	  finalize: function() {
	  	globals.mod_js_core_testLog_html += '</body></html>'
	  },
	  message: function(message) {
			globals.mod_js_core_testMessage = message;
	  },
	  summary: function(summary) {
	    globals.mod_js_core_testSummary = summary;
	    globals.mod_js_core_testLog_html = globals.mod_js_core_testLog_html.replace('#{SUMMARY}', '<h4 style="color:maroon;">' + summary + '</h4>')
	  },
	  _toHTML: function(txt) {
	    return txt.escapeHTML().replace(/\n/g,"<br/>");
	  }
	}		
}

/**
 * @properties={typeid:24,uuid:"268170b7-e63d-4213-b66e-5f0855b1a742"}
 */
function mod_js_core_extend_TestUnitRunner()
{
	Test.Unit.Runner = Class.create();
	Test.Unit.Runner.prototype = {
	  initialize: function(testcases) {
	    this.options = Object.extend({
	      testLog: 'testlog'
	    }, arguments[1] || {});
	    if(this.options.tests) {
	      this.tests = [];
	      for(var i = 0; i < this.options.tests.length; i++) {
	        if(/^test/.test(this.options.tests[i])) {
	          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
	        }
	      }
	    } else {
	      if (this.options.test) {
	        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
	      } else {
	        this.tests = [];
	        for(var testcase in testcases) {
	          if(/^test/.test(testcase)) {
	            this.tests.push(
	               new Test.Unit.Testcase(
	                 this.options.context ? ' -> ' + this.options.titles[testcase] : testcase, 
	                 testcases[testcase], testcases["setup"], testcases["teardown"]
	               ));
	          }
	        }
	      }
	    }
	    this.currentTest = 0;
	    this.logger = new Test.Unit.Logger(this.options.testLog);
	    this.runTests.bind(this);
	    this.runTests();
	  },
	  // Returns:
	  //  "ERROR" if there was an error,
	  //  "FAILURE" if there was a failure, or
	  //  "SUCCESS" if there was neither
	  getResult: function() {
	    var hasFailure = false;
	    for(var i=0;i<this.tests.length;i++) {
	      if (this.tests[i].errors > 0) {
	        return "ERROR";
	      }
	      if (this.tests[i].failures > 0) {
	        hasFailure = true;
	      }
	    }
	    if (hasFailure) {
	      return "FAILURE";
	    } else {
	      return "SUCCESS";
	    }
	  },
	  postResults: function() { },
	  runTests: function() {
	    var test = this.tests[this.currentTest];
	    if (!test) {
	      // finished!
	      this.postResults();
	      this.logger.summary(this.summary());
	      this.logger.finalize();
	      return;
	    }
	    if(!test.isWaiting) {
	      this.logger.start(test.name);
	    }
	    test.run();
	    if(test.isWaiting) {
	      this.logger.message("Waiting for " + test.timeToWait + "ms");
	      this.runTests.bind(this);
	    } else {
	      this.logger.finish(test.status(), test.summary());
	      this.currentTest++;
	      // tail recursive, hopefully the browser will skip the stackframe
	      this.runTests();
	    }
	  },
	  summary: function() {
	    var assertions = 0;
	    var failures = 0;
	    var errors = 0;
	    var messages = [];
	    for(var i=0;i<this.tests.length;i++) {
	      assertions +=   this.tests[i].assertions;
	      failures   +=   this.tests[i].failures;
	      errors     +=   this.tests[i].errors;
	    }
	    return (
	      (this.options.context ? this.options.context + ': ': '') + 
	      this.tests.length + " tests, " + 
	      assertions + " assertions, " + 
	      failures   + " failures, " +
	      errors     + " errors");
	  }
	}
}

/**
 * @properties={typeid:24,uuid:"28736d80-07f0-4266-a305-ab0762fabd9f"}
 */
function mod_js_core_init()
{
	var initTesting = arguments[0] || true;
	
	globals.mod_js_core_extend_Object();
	globals.mod_js_core_extend_Enumerable();
	globals.mod_js_core_extend_Globals();
	globals.mod_js_core_extend_Class();
	globals.mod_js_core_extend_Array();
	globals.mod_js_core_extend_Hash();
	globals.mod_js_core_extend_Function();
	globals.mod_js_core_extend_String();
	globals.mod_js_core_extend_RegExp();
	globals.mod_js_core_extend_Number();
	globals.mod_js_core_extend_ObjectRange();
	globals.mod_js_core_extend_Date();
	
	globals.mod_js_core_extend_Servoy();
	
	ModJsCore = {};
	ModJsCore.version = globals.mod_js_core_version();
	ModJsCore.extended = true;
	ModJsCore.extended_TestUnit = false;
	ModJsCore.testMethodPrefix = '_test_';

	if(initTesting)
	{
		globals.mod_js_core_init_TestUnit();
	}
}

/**
 * @properties={typeid:24,uuid:"674ee71e-26d4-4227-8be5-dcae0f7b2224"}
 */
function mod_js_core_extend_Servoy()
{
	/*	Servoy specific extensions and global methods
		*/

	// Platform tests
	$platform = { };
	$platform.isWindows = function() { return utils.stringMiddle(application.getOSName(),1,7) == "Windows"; };
	$platform.isMac = function() { return utils.stringLeftWords(application.getOSName(), 1) == "Mac"; };
		
	// foundset convenience methods
	$fs = { };

	// return current record from foundset
	$fs.rec = function(fs) {
		return fs.getRecord(fs.getSelectedIndex());
	};
	
	// loop over foundset and run function on each record
	$fs.loop = function(fs, block) {
		for(var ix=1; ix <= databaseManager.getFoundSetCount(fs); ix++)
		{
			var rec = fs.getRecord(ix);
			block(rec);
		}
	};
	
	// Form convenience methods
	$frm = { };
	
	// return current record from form's foundset
	$frm.rec = function(frm) {
		return $fs.rec(frm.foundset);
	};
	
	// loop over the elements on a form, alternately using regex name filter
	$frm.elementLoop = function(frm) {
		var filter = arguments.length > 2 ? arguments[1] : /.*/;
		var block = arguments.length > 2 ? arguments[2] : arguments[1];
		
		for(var ix=0; ix < frm.elements.length; ix++)
		{
			var elem = frm.elements[ix];
			if(filter.match(elem.getName()))
				block(elem);
		}
	};
}

/**
 * @properties={typeid:24,uuid:"8005b4e9-09c6-478d-8771-4f1bd0b928b6"}
 */
function mod_js_core_init_TestUnit()
{
	globals.mod_js_core_extend_TestUnit();
	globals.mod_js_core_extend_TestUnitLogger();
	globals.mod_js_core_extend_TestUnitRunner();
	globals.mod_js_core_extend_TestBDD();
	
	ModJsCore.extended_TestUnit = true;
}

/**
 * @properties={typeid:24,uuid:"df98a841-bd14-4234-8987-379e8620ad08"}
 */
function mod_js_core_isEnvExtended()
{
		// Test if the current runtime has been extended
		if(ModJsCore && ModJsCore.extended) return true;
		return false;
}

/**
 * @properties={typeid:24,uuid:"eb721e09-4bc4-47de-b67e-fd612f8a75b3"}
 */
function mod_js_core_version()
{
		return '0.5';
}

/**
 * @properties={typeid:24,uuid:"a800bee2-d13c-422b-86f6-0aa5e9291b92"}
 */
function mod_js_core_extend_Number()
{
	Object.extend(Number.prototype, {
	  toColorPart: function() {
	    return this.toPaddedString(2, 16);
	  },
	
	  succ: function() {
	    return this + 1;
	  },
	
	  times: function(iterator) {
	    $R(0, this, true).each(iterator);
	    return this;
	  },
	
	  toPaddedString: function(length, radix) {
	    var string = this.toString(radix || 10);
	    return '0'.times(length - string.length) + string;
	  },
	
	  toJSON: function() {
	    return isFinite(this) ? this.toString() : 'null';
	  }
	});
}

/**
 * @properties={typeid:24,uuid:"b0d9e8cd-93b4-45e5-abd4-dd82f61daf54"}
 */
function mod_js_core_extend_RegExp()
{
	RegExp.prototype.match = RegExp.prototype.test;
	
	RegExp.escape = function(str) {
	  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
	};
}

/**
 * @properties={typeid:24,uuid:"65a253b8-64ea-4a69-a502-1ca0955f7ae7"}
 */
function mod_js_core_extend_ObjectRange()
{
	var ObjectRange = Class.create(Enumerable, {
	  initialize: function(start, end, exclusive) {
	    this.start = start;
	    this.end = end;
	    this.exclusive = exclusive;
	  },
	
	  _each: function(iterator) {
	    var value = this.start;
	    while (this.include(value)) {
	      iterator(value);
	      value = value.succ();
	    }
	  },
	
	  include: function(value) {
	    if (value < this.start)
	      return false;
	    if (this.exclusive)
	      return value < this.end;
	    return value <= this.end;
	  }
	});
	
	$R = function(start, end, exclusive) {
	  return new ObjectRange(start, end, exclusive);
	};
}
